﻿/*
 * @author: wizardc
 */

namespace Dou.Utils
{
    /// <summary>
    /// 快速校验码生成工具类
    /// </summary>
    public static class CRC
    {
        private static readonly uint[] _crcTable = MakeCRCTable();

        private static uint[] MakeCRCTable()
        {
            var numArray = new uint[256];
            for (uint i = 0; i < 256; ++i)
            {
                var num = i;
                for (uint j = 0; j < 8; ++j)
                {
                    if (((int) num & 1) == 1)
                    {
                        num = 3988292384U ^ num >> 1;
                    }
                    else
                    {
                        num >>= 1;
                    }
                }
                numArray[(int) i] = num;
            }
            return numArray;
        }

        /// <summary>
        /// 获取 CRC32 码
        /// </summary>
        public static uint CRC32(byte[] bytes, uint start = 0, uint len = 0)
        {
            if (start >= bytes.Length)
            {
                start = (uint) bytes.Length;
            }
            if (len == 0)
            {
                len = (uint) bytes.Length - start;
            }
            if (len + start > bytes.Length)
            {
                len = (uint) bytes.Length - start;
            }
            var num = uint.MaxValue;
            for (var i = start; i < len; ++i)
            {
                num = _crcTable[((int) num ^ bytes[(int) i]) & byte.MaxValue] ^ num >> 8;
            }
            return num ^ uint.MaxValue;
        }

        /// <summary>
        /// 获取 Adler32 码，执行效率比 CRC32 码更高，但是校验能力较 CRC32 码低一些
        /// </summary>
        public static uint Adler32(byte[] bytes)
        {
            return Adler32(bytes, bytes.Length);
        }
        
        public static uint Adler32(byte[] bytes, int bytesLen, uint start = 0, uint len = 0)
        {
            if (start >= bytesLen)
            {
                start = (uint) bytesLen;
            }
            if (len == 0)
            {
                len = (uint) bytesLen - start;
            }
            if (len + start > bytesLen)
            {
                len = (uint) bytesLen - start;
            }
            uint num1 = 1;
            uint num2 = 0;
            for (var i = start; i < start + len; ++i)
            {
                num1 = (num1 + bytes[(int) i]) % 65521U;
                num2 = (num1 + num2) % 65521U;
            }
            return num2 << 16 | num1;
        }
    }
}
